"""
    利用基本面因子在不同行业中进行选股，测试结果很差。
    - 电力设备：季度净利润增速，季度ROE，季度研发费用占营收比例，季度合同负债占营收比例，现金净流入金额占总资产比例
    - 煤炭：季度ROE
    - 医药生物：季度ROE
    - 建筑材料：季度ROE
    - 交通运输： 季度ROE
    - 机械设备：季度净利润增速，季度ROE
    - 有色金属：季度ROE
    - 汽车：季度净利润增速，季度合同负债
    - 通信：现金净流入占总资产比例
    - 电子：季度净利润增速，季度研发费用占营收比例，季度合同负债
    - 轻工制造：季度净利润增速，季度ROE
    - 食品饮料：季度ROE，现金净流入占总资产比例
    - 公用事业： 季度净利润增速，季度ROE，季度合同负债
    - 银行：季度ROE
    - 家用电器：季度ROE
    - 钢铁：季度净利润增速
"""
import pandas as pd
from utilities.utilities_strategy import stockPool_raw
from backTestAnaly.bactestEngine import BackTestEngine
from data_resource.data_bases import engine
from utilities.utilities_statics import factor_standardize
from utilities.utilities_func import set_limit_type
from backTestAnaly.dataLoader import DataLoader
from strategy.pb_roe import get_prev_financialdate, get_rebalance
from factorPool.momentums import period_return


class FundamentalSeeking(BackTestEngine):
    def __init__(self, price: DataLoader, rebalance_dates: list, financial_date: dict,
                 benchmark_data: pd.DataFrame = None,
                 cash=100000):
        """
            price: 前复权行情价格参数，columns=[trading, code, close, open, volume]
        """
        super().__init__(data=price, cash=cash, benchmark_data=benchmark_data)  # 注入交易日数据
        self.strategy_pool = None  # 每报告期截面股票池
        self.init_pool = stockPool_raw()  # 初始股票池, columns=[code, industry]
        self.rebalance_dates = rebalance_dates  # 调仓日期, 通过get_rebalance()函数获取
        self.financial_date = financial_date  # 报告期数组 {key: 交易日, value: 报告期}，若报告期为非交易日，则key为后一交易日
        # 设置因子数据, columns = [code, trading, signal]

        # 组织价值的变化
        self.total_portvalue = {}

        # 每期选股结果记录
        self.historical_picks = []

        # 计算动量因子
        sql1 = """
        select a.ticker as code, a.trade_date as trading, a.close, a.open, b.l1_name as industry_code, c.circ_mv
        from quant_research.market_daily_ts as a
        left join quant_research.sw_industry_constituent as b on a.ticker=b.ts_code
        left join quant_research.indicator_daily as c on a.ticker=c.code and a.trade_date=c.trade_date
        
        where a.ticker in (
            select con_code from quant_research.index_constituent where trade_date=(
                select max(trade_date) from quant_research.index_constituent
            ) and index_code in ('000852.SH', '000906.SH')
        ) and a.trade_date > '2014-01-01'
        order by a.trade_date;
        """
        raw = pd.read_sql(sql1, con=engine)
        raw['momentum_3M'] = raw.groupby('code')['close'].transform(lambda x: period_return(
            x, 40, 20))
        raw['momentum_3M'] = raw.groupby('trading', as_index=False)['momentum_3M'].transform(
            lambda x: factor_standardize(x))
        self.momentum_3M = raw

        # 反转因子
        # sql1 = """
        # select code, trading, signal
        # from quant_research."factor_returnWgt"
        # where code in (
        #     select con_code from quant_research.index_constituent where trade_date=(
        #         select max(trade_date) from quant_research.index_constituent
        #     ) and index_code in ('000852.SH', '000906.SH')
        # ) and trading > '2014-01-01'
        # order by trading;
        # """
        # self.reverse_factor = pd.read_sql(sql1, con=engine)

    def __next__(self):
        # 交易主体
        result = super().__next__()  # 获取父类迭代器返回的：下一数据帧索引

        # 处理未完成订单
        if (len(self.paramsPipe.stocks_unsettled) > 0) and (self.today not in self.rebalance_dates):
            _unsettled_stocs = self.paramsPipe.get_stocks_unsettled()
            _unsettled_stocs.sort_values(by='direction', ascending=True, inplace=True)
            _unsettled = []
            for i in range(len(_unsettled_stocs)):
                _code = _unsettled_stocs.iloc[i, 0]
                _size = _unsettled_stocs.iloc[i, 1]
                _price = _unsettled_stocs.iloc[i, 2]
                _direction = _unsettled_stocs.iloc[i, 3]
                _limit_type = set_limit_type(_code)

                # 下单
                if _direction == -1:
                    self.order.sell(_code, result, _limit_type, _price, _size)
                    if self.order.order_status != 1:
                        _unsettled.append((_code, _size, _price, _direction))

                elif _direction == 1:
                    self.order.buy(_code, result, _limit_type, _price, _size)
                    if self.order.order_status != 1:
                        _unsettled.append((_code, _size, _price, _direction))
            # 对未完成订单进行替换
            self.paramsPipe.update_stocks_unsettled(_unsettled)

        """策略主体, 直接买入行业财务优选股票"""
        if (self.today in self.rebalance_dates) and (self.strategy_pool is not None):
            self.paramsPipe.update_stocks_unsettled([])
            _f = pd.DataFrame(data={
                'code': self.strategy_pool,
                'target_weight': [0.85 / len(self.strategy_pool)] * len(self.strategy_pool)
            })
            _f['limit_type'] = _f['code'].apply(lambda c: set_limit_type(c))
            self.historical_picks.append({self.today: _f})
            self.order.bulk_order_targetWeight(_f, result)

    def after_trading(self):
        # 在财报披露季，获取股票池
        today = self.today.strftime("%Y-%m-%d")
        if today in self.financial_date.keys():
            report_date = self.financial_date[today]
            _sql = f"""
                -- 中证800+1000股票池，限制市值大于50亿
                select a.code, a.industry_code, a.signal_growth
                from quant_research.factor_growth as a
                left join quant_research.indicator_daily as b on a.code=b.code and b.trade_date='{today}'
                where a.end_date = '{report_date}' and b.circ_mv>=300000 and a.code in (
                    select con_code from quant_research.index_constituent where trade_date=(
                        select max(trade_date) from quant_research.index_constituent
                    ) and index_code in ('000852.SH', '000906.SH')
                )
                order by signal_growth desc limit 300;
            """
            raw = pd.read_sql(_sql, engine)
            if raw.empty:
                return
            else:
                self.strategy_pool = raw['code'].to_list()

            # 动量共振（3个月)
            _momentum = self.momentum_3M[self.momentum_3M['trading'] == self.today]

            # # 反转因子
            # _reverse = self.reverse_factor[self.reverse_factor['trading'] == self.today]
            # 获取属于综合成长因子取值的个股
            if self.strategy_pool is None:
                return
            else:
                _momentum_pool = _momentum[_momentum['code'].isin(self.strategy_pool)].copy()
                # _reverse_pool = _reverse[_reverse['code'].isin(self.strategy_pool)].copy()
                _momentum_pool.sort_values(by='momentum_3M', ascending=False, inplace=True)
                # _reverse_pool.sort_values(by='signal', ascending=True, inplace=True)
                # self.strategy_pool = _reverse_pool['code'].to_list()[:30]
                self.strategy_pool = _momentum_pool['code'].to_list()[:30]


def strategy_run():
    # 获取行情数据 2024-01-01至今，中证800+中证1000成分股
    sql1 = """
        select trade_date as trading, code, close, open, vol as volume, high, low
        from quant_research.market_daily_qfq
        where quant_research.market_daily_qfq.code in (
            -- 用中证1000, 中证800成分股做为股票池
            select con_code from quant_research.index_constituent where trade_date=(
                select max(trade_date) from quant_research.index_constituent
            ) and index_code in ('000852.SH', '000906.SH')
        ) and trade_date>='2015-08-31' and trade_date < '2025-08-30'
        order by trade_date;
    """
    # 获取bench_mark信息
    sql2 = """
        select trade_date as trading, close
        from quant_research.index_daily
        where ts_code='000300.SH' and trade_date >= '2015-08-31' and trade_date < '2025-08-30'
        order by trade_date;
    """
    market_data = pd.read_sql(sql1, engine)
    bench_mark = pd.read_sql(sql2, engine)
    # 准备数据管线
    price = DataLoader(market_data=market_data)
    # 报告期数组
    financial_dates_dict = get_prev_financialdate(2014, 2025)
    # 补充2025年
    financial_dates_dict['2025-03-31'] = '2025-03-31'
    financial_dates_form = []
    # 获取调仓日期
    for i in range(2014, 2025):
        financial_dates_form.append(f'{i}-04-30')
        financial_dates_form.append(f'{i}-08-31')
        financial_dates_form.append(f'{i}-10-31')

    rebalances = get_rebalance(financial_dates_form)
    # 注入回测模型
    _strategy = FundamentalSeeking(price=price, rebalance_dates=rebalances, financial_date=financial_dates_dict,
                                   cash=1000000000, benchmark_data=bench_mark)
    _strategy.run()
    return _strategy


def main():
    # 运行策略
    strategy = strategy_run()
    # 获取回测结果
    print("---------------- 1.组合分析 -----------------")
    strategy.analysis.port_analysis()
    print(strategy.analysis.port_indicators)
    strategy.analysis.plot_return(save_path='fundamental_picks')


if __name__ == '__main__':
    main()
